package dev.ece.suit.loader;

import java.io.InputStream;
import java.nio.FloatBuffer;
import java.util.Map;

import org.lwjgl.opengl.GL20;

import dev.ece.core.light.MaterialLocationHandler;
import dev.ece.core.shader.Shader;
import dev.ece.core.texture.FBOS;
import dev.ece.suit.utils.ShaderFunctions;
import dev.ece.util.tools.BufferBuilder;

public class ModelShader extends Shader {
	
	public static final int MAX_SKELETON_COUNT = 64;
	
	private static ModelShader defaultModelShader;
	
	public static ModelShader getDefaultModelShader() {
		if(defaultModelShader == null) {
			defaultModelShader = new ModelShader(
					ModelShader.class.getClassLoader().getResourceAsStream("shaders/defer/model3d.vert"),
					ModelShader.class.getClassLoader().getResourceAsStream("shaders/defer/model3d.frag"),
					ShaderFunctions.getFunctions());
		}
		return defaultModelShader;
	}
	
	/**
	 * 最多64个关节
	 */
	private FloatBuffer localSkeletonMatrixBuffer = BufferBuilder.buildFloatBuffer(MAX_SKELETON_COUNT * 16 * 4);
	
	private FloatBuffer normalSkeletonMatrixBuffer = BufferBuilder.buildFloatBuffer(MAX_SKELETON_COUNT * 16 * 4);
	
	private int localSkeletonMatrixHandler;
	
	private int normalSkeletonMatrixHandler;
	
	private MaterialLocationHandler materialLocationHanlder;
	
	private int textureHandler;
	
	private int eceGlAlbedoSpecHandler;
	
	private int existTextureHandler;
	
	public ModelShader(StringBuffer vertexShaderString,
			StringBuffer pixelShaderString, Map<String, String> functions) {
		super(vertexShaderString, pixelShaderString, functions);
	}
	
	public ModelShader(InputStream vertexShaderInputStream,
			InputStream pixelShaderInputStream, Map<String, String> functions) {
		super(vertexShaderInputStream, pixelShaderInputStream, functions);
	}
	
	@Override
	protected String getVertexHeader() {
		StringBuffer s = new StringBuffer();
		s.append(getPixelHeader());
		s.append(super.getVertexHeader());
		return s.toString();
	}
	
	@Override
	protected String getPixelHeader() {
		StringBuffer s = new StringBuffer();
		s.append(Material.getMaterialStructString());
		return s.toString();
	}
	
	@Override
	protected void onInitializeHandler(int program) {
		localSkeletonMatrixHandler = createLocalSkeletonMatrixHandler(program);
		normalSkeletonMatrixHandler = createNormalSkeletonMatrixHandler(program);
		materialLocationHanlder = MaterialLocationHandler.newInstance(program, "material.");
		existTextureHandler = GL20.glGetUniformLocation(program, "existTexture");
		textureHandler = GL20.glGetUniformLocation(program, "texture");
		eceGlAlbedoSpecHandler = glGetUniformLocation("eceGlAlbedoSpecTexture");
	}
	
	
	protected int createLocalSkeletonMatrixHandler(int program) {
		return GL20.glGetUniformLocation(program, "ece_LocalSkeletonMatrix");
	}
	
	protected int createNormalSkeletonMatrixHandler(int program) {
		return GL20.glGetUniformLocation(program, "ece_NormalSkeletonMatrix");
	}
	
	@Override
	protected void onBindAttribLocations(int program) {
		GL20.glBindAttribLocation(program, 0, "ece_Vertex");
		GL20.glBindAttribLocation(program, 1, "ece_BoneIds");
		GL20.glBindAttribLocation(program, 2, "ece_Weights");
		GL20.glBindAttribLocation(program, 3, "ece_TexCoord");
		GL20.glBindAttribLocation(program, 4, "ece_Normal");
	}
	
	public void glMaterial(Material material) {
		material.glUniformMaterial(materialLocationHanlder);
		if(material.isExistTexture()) {
			GL20.glUniform1i(existTextureHandler, 1);
			GL20.glUniform1i(textureHandler, material.getTexture().bind(0));
		} else {
			GL20.glUniform1i(existTextureHandler, 0);
		}
	}
	
	public void glDeferHandler(FBOS defer) {
		GL20.glUniform1i(eceGlAlbedoSpecHandler, defer.getTextures()[2].bind(1));
	}
	
	public void glJointsLocalMatrix(Joint<?>[] joints) {
		if(joints != null) {
			for(Joint<?> joint:joints) {
				joint.getFinalTransform().store(localSkeletonMatrixBuffer);
				joint.getNormalTransform().store(normalSkeletonMatrixBuffer);
			}
			this.localSkeletonMatrixBuffer.rewind();
			this.normalSkeletonMatrixBuffer.rewind();
			GL20.glUniformMatrix4fv(localSkeletonMatrixHandler, false, this.localSkeletonMatrixBuffer);
			GL20.glUniformMatrix4fv(normalSkeletonMatrixHandler, false, this.normalSkeletonMatrixBuffer);
		}
	}
	

	@Override
	protected String getVersion() {
		return "#version 330 core";
	}

	public void setLocalSkeletonMatrixBuffer(
			FloatBuffer localSkeletonMatrixBuffer) {
		this.localSkeletonMatrixBuffer = localSkeletonMatrixBuffer;
	}

	public void setNormalSkeletonMatrixBuffer(FloatBuffer normalSkeletonMatrixBuffer) {
		this.normalSkeletonMatrixBuffer = normalSkeletonMatrixBuffer;
	}

	@Override
	public void clearup() {
		GL20.glDeleteShader(localSkeletonMatrixHandler);
		GL20.glDeleteShader(normalSkeletonMatrixHandler);
		GL20.glDeleteShader(textureHandler);
		materialLocationHanlder.clearup();
		super.clearup();
	}
	
}
